using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Media;
using System.Windows.Threading;
using System.Linq;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public static WriteableBitmap wBitmap = new WriteableBitmap(450, 400, 96.0, 96.0, PixelFormats.Rgb24, null);
        static Int32Rect rect = new Int32Rect(0, 0, wBitmap.PixelWidth, wBitmap.PixelHeight);
        
        static int bytesPerPixel = (wBitmap.Format.BitsPerPixel + 7) / 8;
        static int stride = wBitmap.PixelWidth * bytesPerPixel;
        static int colorArraySize = stride * wBitmap.PixelHeight;
        static byte[] colors = new byte[colorArraySize];
        static int[] persistence = new int[256];

        private Int64 colorArrayPosition { get; set; }
        private byte color1 { get; set; }
        private byte color2 { get; set; }
        private byte color3 { get; set; }
        private int pass { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            colorArrayPosition = 0;
            pass = 0;
            color1 = 0;
            color2 = 0;
            color3 = 0;
            InitNoise();
            EventHandler timerCallback = new EventHandler(UpdateImage);
            DispatcherTimer render = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 10), DispatcherPriority.Normal, timerCallback, Dispatcher.CurrentDispatcher);
            render.Start();
        }

        private void InitNoise()
        {
            Random rand = new Random();
            for (int i = 0; i < persistence.Length; i++)
            {
                persistence[i] = rand.Next(persistence.Length);
            }
        }

        private void UpdateImage(object o, EventArgs e)
        {
            if (cbRandom.IsChecked.HasValue && cbRandom.IsChecked.Value == true)
            {
                Random value = new Random();
                value.NextBytes(colors);
                wBitmap.WritePixels(rect, colors, stride, 0);
            }
            
            if (cbMandlebrot.IsChecked.HasValue && cbMandlebrot.IsChecked.Value == true)
            {
                wBitmap.WritePixels(rect, FillMandlebrot(), stride, 0);
            }

            if(cbTest.IsChecked.HasValue && cbTest.IsChecked.Value == true)
            {
                //byte[] testcolors = new byte[colorArraySize];
                //testcolors = Test();
                wBitmap.WritePixels(rect, Test(), stride, 0);
            }

            if (cbPerlin.IsChecked.HasValue && cbPerlin.IsChecked.Value == true)
            {
                Perlin(1);
                //wBitmap.WritePixels(rect,, stride, 0);
            }

            if (cbColorPicker.IsChecked.HasValue && cbColorPicker.IsChecked.Value == true)
            {
                byte red = (byte)slider1.Value;
                byte green = (byte)slider2.Value;
                byte blue = (byte)slider3.Value;

                byte[] color = new byte[3];
                color[0] = red;
                color[1] = green;
                color[2] = blue;

                for (int i = 0; i < colors.Length; i += bytesPerPixel)
                {
                    colors[i] = color[0];
                    colors[i + 1] = color[1];
                    colors[i + 2] = color[2];
                }

                wBitmap.WritePixels(rect, colors, stride, 0);
            }

            Image1.Source = wBitmap;
        }

        private double Perlin(float x)
        {
            double total = 0;
            int persistence = 1;
            int num_octaves = 1;

            for (int i = 0; i < num_octaves; i++)
            {
                double freq = Math.Pow(2, i);
                double amp = Math.Pow(persistence, i);

                total += interpolatedNoise1(x * (float)freq, false) * amp;
            }
            return total;
        }

        private double noise(int x)
        {
            x = (x << 13) ^ x;
            return (-1.0 - ( (x *(x * x * 15731 + 789221) + 137612589) & 0x7fffffff) / 1073741824.0);
        }

        private double smoothNoise1(float x)
        {
            return noise((int)x)/2 + noise((int)x-1)/4 + noise((int)x+1)/4;
        }

        private double interpolatedNoise1(float x, bool cosineLerp)
        {
            int x1 = (int)x;
            double fracX = x - x1;

            double v1 = smoothNoise1(x);
            double v2 = smoothNoise1(x + 1);

            if (cosineLerp)
                return clerp(v1, v2, fracX);

            return lerp(v1, v2, fracX);
        }
        private double fade(double t)
        {
            return t * t * t * (t * (t * 6 - 15) + 10);
        }
        private double lerp(double a, double b, double t)
        {
            return (a * t + (b - a));
        }

        // cosine interpolation
        private double clerp(double a, double b, double t)
        {
            double ft = t * Math.PI;
            double f = (1 - Math.Cos(ft)) * 0.5;
            return a * (1-f) + b*f;
        }

        private void setPixel(int x, int y, Color color)
        {
            int pos = (y * stride) + x;
            colors[pos] = color.R;
            colors[pos + 1] = color.G;
            colors[pos + 2] = color.B;
        }

        private void setPixel(int x, int y, byte r, byte g, byte b)
        {
            int pos = (y * stride) + x;
            colors[pos] = r;
            colors[pos + 1] = g;
            colors[pos + 2] = b;
        }

        private byte[] Test()
        {
            int width = wBitmap.PixelWidth * 3;

            for (int h = 0; h < wBitmap.PixelHeight; h++)
            {
                for (int w = 0; w < width; w+= 3)
                {
                    setPixel(w, h, 255, 255, 200);
                }
            }
            return colors;
        }

        private byte[] FillMandlebrot()
        {
            // range for n such that |z[n]| < max            
            double x, y, x1, y1, xx, xmin, xmax, ymin, ymax;
            xmax = MandelZoomMax.Value / 10;
            ymax = MandelZoomMax.Value / 10;
            xmin = -MandelZoomMin.Value / 10;
            ymin = -MandelZoomMin.Value / 10;
            x = xmin;
            double dy = (ymax - ymin) / wBitmap.PixelHeight;
            double dx = (xmax - xmin) / wBitmap.PixelWidth;
            int looper;
            for (int s = 0; s < wBitmap.PixelWidth * 3; s+=3)
            {
                y = ymin;
                for (int z = 0; z < wBitmap.PixelHeight; z++)
                {
                    x1 = 0;
                    y1 = 0;
                    looper = 0;
                    while (looper < 100 && Math.Sqrt((x1 * x1) + (y1 * y1)) < 2)
                    {
                        looper++;
                        xx = (x1 * x1) - (y1 * y1) + x;
                        y1 = 2 * y1 * x1 + y;
                        x1 = xx;
                    }

                    double percentage = looper / 100.0;
                    int colorVal = (int)(percentage * 255);
                    byte red = 0x0, green = 0x0, blue = 0x0;
                    if (colorVal > 0 && colorVal < 50)
                    {
                        red = 0;
                        green = 0;
                        blue = 0;
                    }
                    else if (colorVal > 50 && colorVal < 100)
                    {
                        red = 125;
                        green = 255;
                        blue = 0;
                    }
                    else if (colorVal > 100 && colorVal < 150)
                    {
                        red = 255;
                        green = 255;
                        blue = 0;
                    }
                    else if (colorVal > 150 && colorVal < 200)
                    {
                        red = 0;
                        green = 0;
                        blue = 255;
                    }
                    else if (colorVal > 200 && colorVal < 256)
                    {
                        red = (byte)slider1.Value;
                        green = (byte)slider2.Value;
                        blue = (byte)slider3.Value;
                    }

                    setPixel(s, z, red, green, blue);
                    y += dy;
                }
                x += dx;
            }
            
            return colors;
        }

        private void Reset()
        {
            colors = new byte[colorArraySize];
            colorArrayPosition = 0;
            color1 = 0;
            color2 = 0;
            color3 = 0;
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            Window1 window3D = new Window1();
            window3D.Show();
        }
    }
}
